/*
 *  CSMACAInitiator.cpp
 *  simulator
 *
 *  Created by  on 4/4/12.
 *  Copyright 2012 __MyCompanyName__. All rights reserved.
 *
 */

#include "CSMACAInitiator.hpp"
#include "Settings.hpp"
#include "Medium.hpp"
#include "Types.hpp"
#include "Message.hpp"
#include "Statistics.hpp" 
#include <stdlib.h>

using namespace Sim ;


CSMACAInitiator :: CSMACAInitiator ( ID const & name , ID const & inTarget , UInteger inLambdaScalar )
:	StateMachineNode<CSMACAInitiator>	( name , idle )
,	target				( inTarget )
,	lambdaScalar		( inLambdaScalar )
,	message				( 0 )
,	statistics			( std::string("Node ")+name , sIdle )
{
	registerStateCallback ( idle , &CSMACAInitiator::update_idle , "Waiting for packet to send." ) ;
	registerStateCallback ( checkMed , &CSMACAInitiator::update_checkMed , "Checking medium if not busy" ) ;
	registerStateCallback ( waitToSend , &CSMACAInitiator::update_waitToSend , "Medium is busy. Waiting until idle." ) ;
	registerStateCallback ( waitDIFS , &CSMACAInitiator::update_waitDIFS , "Waiting for DIFS." ) ;
	registerStateCallback ( waitPauseDIFS , &CSMACAInitiator::update_waitPauseDIFS , "DIFS timer paused." ) ;
	registerStateCallback ( sendData , &CSMACAInitiator::update_sendData , "Sending data." ) ;
	registerStateCallback ( waitACK , &CSMACAInitiator::update_waitACK , "Waiting for acknowledgement." ) ;
	registerStateCallback ( packetCollision , &CSMACAInitiator::update_packetCollision , "Packet Collided." ) ;
	registerStateCallback ( backoff , &CSMACAInitiator::update_backoff , "Backing off." ) ;
	createTraffic = Settings::shared().poissonSlotCount(lambdaScalar);
}
CSMACAInitiator :: ~CSMACAInitiator (){}

void	CSMACAInitiator :: update_idle						( UInteger currentTime , Medium::MsgSet const & currentReception ) {
	if(createTraffic == currentTime){
		setState(checkMed);
	}
	else{
		setState(idle);
	}
}
void	CSMACAInitiator :: update_checkMed					( UInteger currentTime , Medium::MsgSet const & currentReception ){
	if(currentReception.size() == 0){
		setState(sendData);
	}
	else{
		setState(waitToSend);
	}
}
void	CSMACAInitiator :: update_waitToSend				( UInteger currentTime , Medium::MsgSet const & currentReception ){
	if(currentReception.size() == 0){
		createBackoff = (1 + rand()%(Settings::shared().CW0*getMultiplier(true))) + currentTime;
		setState(backoff);
	}
	else {
		setState(waitToSend);
	}

}
void	CSMACAInitiator :: update_waitDIFS					( UInteger currentTime , Medium::MsgSet const & currentReception ){
	if(currentReception.size() != 0){
		setState(waitPauseDIFS);
	}
	else if(createDIFS == currentTime){
		setState(sendData);
	}
	else {
		setState(waitDIFS);
	}

}
void	CSMACAInitiator :: update_waitPauseDIFS			( UInteger currentTime , Medium::MsgSet const & currentReception ){
	if(currentReception.size() != 0){
		createDIFS++;
		setState(waitPauseDIFS);
	}
	else{
		setState(waitDIFS);
	}
	
}
void	CSMACAInitiator :: update_sendData					( UInteger currentTime , Medium::MsgSet const & currentReception ){
	message = new Message ( identity , target , Message::kTypeData , Settings::shared().dataPacketDuration ) ;
	Medium::shared() . registerMessage ( message ) ;	
	createTimeout = Settings::shared().dataPacketDuration + Settings::shared().controlPacketDuration + Settings::shared().SIFSDuration + currentTime + 1;
	statistics << sTXData ;
	setState(waitACK);
}
void	CSMACAInitiator :: update_waitACK					( UInteger currentTime , Medium::MsgSet const & currentReception ){
	int flag = 0;
	if(createTimeout == currentTime){
		setState(packetCollision);
	}
	else if(currentReception.size() != 0){
		std::list<Message*> current = currentReception;
		std::list<Message*>::iterator	here	=	current.begin() ;
		std::list<Message*>::iterator	end		=	current.end() ;
		while ( here != end )
		{
			Message & m = * (*(here++)) ;
			if(m.target == identity && m.lastSlot == currentTime){//Message target was intended for identity. Message Success.
				flag = 1;
				m.status = Message::kStatusSuccessful ;
				message -> release();
				message = 0;
				break;
			}
		}
		if(flag == 1){
			getMultiplier(false);
			createTraffic = Settings::shared().poissonSlotCount(lambdaScalar) + currentTime;//Packet Traffic Creation
			statistics << sIdle ;
			setState(idle);
		}
		else {
			setState(waitACK);
		}
	}
	else{
		setState(waitACK);
	}
}
void	CSMACAInitiator :: update_packetCollision			( UInteger currentTime , Medium::MsgSet const & currentReception ){
	statistics << sTXDataFail ;
	message -> release();
	message = 0;
	getMultiplier(false);//Collision of packets occured
	createBackoff = (1 + rand()%(Settings::shared().CW0*getMultiplier(true))) + currentTime;
	setState(backoff);
}
void	CSMACAInitiator :: update_backoff					( UInteger currentTime , Medium::MsgSet const & currentReception ){
	if(createBackoff == currentTime){
		createDIFS = Settings::shared().DIFSDuration + currentTime;
		setState(waitDIFS);
	}
	else{
		setState(backoff);
	} 
}

int CSMACAInitiator :: getMultiplier						(bool selection){
	static int	multiplier = 1;
	if(selection == false){
		if(multiplier < 10){//If collision occurs double contention window
			multiplier++;
		}
	}
	else{
		multiplier = 1;
	}
	return multiplier;	
}
